#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>   
#include <unistd.h>

//为什么不直接用stl中的队列充当共享资源？因为stl设计没有考虑线程安全，使用的是单线程保证效率
//所以需要我们对这个队列进行加锁
template <class T>
class BlockQueue
{
    static const int defaultNum=5;
public:
    BlockQueue(int maxcapa=defaultNum):maxcapa_(maxcapa)
    {
        pthread_mutex_init(&mutex_,nullptr);
        pthread_cond_init(&c_cond_,nullptr);
        pthread_cond_init(&p_cond_,nullptr);
        // low_water_=maxcapa_/3;
        // high_water_=(maxcapa_*2)/3;
    }

    T pop()
    {
        pthread_mutex_lock(&mutex_);

        //while防止伪唤醒
        while(q_.size()==0)
        {
            pthread_cond_wait(&c_cond_,&mutex_);
        }

        T out=q_.front();                           //消费前确保消费条件满足
        q_.pop();
        //if(q_.size()<low_water_) pthread_cond_signal(&p_cond_);      

        pthread_cond_signal(&p_cond_);              //消费者消费了一个，且队列受锁的保护，期间没有其它执行流再能进行修改了，一定能保证队列中有空间能让生产者生产，此时可以唤醒生产者去生产
        pthread_mutex_unlock(&mutex_);              

        return out;
    }

    void push(const T& in)
    {
        pthread_mutex_lock(&mutex_);                //加锁防止push的时候有人在pop等修改，stl容器不保证线程安全

        //while防止伪唤醒
        while(q_.size()==maxcapa_)                      //注意这里的判断不能放在加锁之前，因为判断也访问临界资源了，也要确保安全
        {
            pthread_cond_wait(&p_cond_,&mutex_);    //1.调用的时候，自动释放锁   2.被唤醒后再把锁拿回来往下执行
        }

        //走到这有两种情况：1.队列没满  2.被唤醒
        q_.push(in);                                //生产前确保生产条件满足
 
        //if(q_.size()>high_water_) pthread_cond_signal(&c_cond_);       
        pthread_cond_signal(&c_cond_);              //生产者生产了一个，且队列受锁的保护，期间没有其它执行流再能进行修改了，一定能保证队列中有数据，此时可以唤醒消费者去消费
        pthread_mutex_unlock(&mutex_);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&c_cond_);
        pthread_cond_destroy(&p_cond_);
    }

private:
    std::queue<T> q_;                //共享资源
    //int mincapa_;                  //队列极小值，默认是0了，这里不写了
    int maxcapa_;                    //队列极大值
    pthread_mutex_t mutex_;

    //生产与消费者不能共用一个条件变量的等待队列，共用一个这样在唤醒的时候就乱了，不知道唤醒的是生产者还是消费者
    pthread_cond_t c_cond_;          //消费者的条件变量
    pthread_cond_t p_cond_;          //生产者的条件变量

    //int low_water_;
    //int high_water_;
};